from rest_framework import exceptions, serializers

from sysreptor.pentests.models import ProjectType, ProjectTypeScope, SourceEnum
from sysreptor.pentests.serializers.common import ImportSerializer, LockInfoSerializer
from sysreptor.utils.configuration import configuration


class ProjectTypeShortSerializer(serializers.ModelSerializer):
    details = serializers.HyperlinkedIdentityField(view_name='projecttype-detail', read_only=True)
    assets = serializers.HyperlinkedIdentityField(view_name='uploadedasset-list', lookup_url_kwarg='projecttype_pk', read_only=True)

    class Meta:
        model = ProjectType
        fields = [
            'id', 'created', 'updated', 'source', 'scope',
            'name', 'language', 'status', 'tags', 'usage_count',
            'details', 'assets',
        ]
        read_only_fields = ['usage_count']


class ProjectTypeDetailSerializer(ProjectTypeShortSerializer):
    lock_info = LockInfoSerializer()
    report_template = serializers.CharField(required=False, allow_blank=True)
    report_styles = serializers.CharField(required=False, allow_blank=True)
    report_preview_data = serializers.DictField(required=False)
    copy_of = serializers.PrimaryKeyRelatedField(read_only=True)

    class Meta(ProjectTypeShortSerializer.Meta):
        fields = ProjectTypeShortSerializer.Meta.fields + [
            'copy_of', 'lock_info',
            'report_template', 'report_styles', 'report_preview_data',
            'report_sections', 'finding_fields', 'finding_ordering', 'finding_grouping',
            'default_notes',
        ]


class ProjectTypeCreateSerializer(ProjectTypeDetailSerializer):
    scope = serializers.ChoiceField(choices=[c for c in ProjectTypeScope.choices if c[0] in [ProjectTypeScope.GLOBAL.value, ProjectTypeScope.PRIVATE.value]])

    def validate_scope(self, value):
        if value == ProjectTypeScope.PRIVATE and not configuration.ENABLE_PRIVATE_DESIGNS:
            raise serializers.ValidationError(f'Scope "{value}" not supported')
        elif value == ProjectTypeScope.GLOBAL and not (self.context['request'].user.is_admin or self.context['request'].user.is_designer):
            raise exceptions.PermissionDenied()
        return value

    def create(self, validated_data):
        scope = validated_data.pop('scope')
        validated_data |= {
            ProjectTypeScope.GLOBAL: {'linked_project': None, 'linked_user': None},
            ProjectTypeScope.PRIVATE: {'linked_project': None, 'linked_user': self.context['request'].user},
        }[scope]
        return super().create(validated_data | {
            'skip_post_create_signal': False,
        })


class ProjectTypePreviewSerializer(serializers.ModelSerializer):
    report_template = serializers.CharField(required=False, allow_blank=True, write_only=True)
    report_styles = serializers.CharField(required=False, allow_blank=True, write_only=True)
    report_preview_data = serializers.DictField(required=False, write_only=True)

    class Meta:
        model = ProjectType
        fields = ['report_template', 'report_styles', 'report_preview_data']


class ProjectTypeRelatedField(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        return ProjectType.objects.only_permitted(self.context['request'].user)


class ProjectTypeImportSerializer(ImportSerializer):
    scope = serializers.ChoiceField(choices=[c for c in ProjectTypeScope.choices if c[0] in [ProjectTypeScope.GLOBAL.value, ProjectTypeScope.PRIVATE.value]], write_only=True)

    def validate_scope(self, value):
        if value == ProjectTypeScope.PRIVATE and not configuration.ENABLE_PRIVATE_DESIGNS:
            raise serializers.ValidationError(f'Scope "{value}" not supported')
        elif value == ProjectTypeScope.GLOBAL and not (self.context['request'].user.is_admin or self.context['request'].user.is_designer):
            raise exceptions.PermissionDenied()
        return value


class ProjectTypeCopySerializer(serializers.ModelSerializer):
    scope = serializers.ChoiceField(choices=[c for c in ProjectTypeScope.choices if c[0] in [ProjectTypeScope.GLOBAL.value, ProjectTypeScope.PRIVATE.value]])

    class Meta:
        model = ProjectType
        fields = ['id', 'name', 'scope']
        extra_kwargs = {
            'name': {'required': False},
        }

    def validate_scope(self, value):
        if value == ProjectTypeScope.PRIVATE and not configuration.ENABLE_PRIVATE_DESIGNS:
            raise serializers.ValidationError(f'Scope "{value}" not supported')
        elif value == ProjectTypeScope.GLOBAL and not (self.context['request'].user.is_admin or self.context['request'].user.is_designer):
            raise exceptions.PermissionDenied()
        return value

    def update(self, instance, validated_data):
        return instance.copy(
            name='Copy of ' + instance.name,
            source=SourceEnum.CREATED,
            usage_count=instance.usage_count,
            **({
                ProjectTypeScope.GLOBAL: {'linked_user': None, 'linked_project': None},
                ProjectTypeScope.PRIVATE: {'linked_user': self.context['request'].user, 'linked_project': None},
            }[validated_data.pop('scope')]),
        )

